package com.gcloud.mesh.dcs.chain;

import com.gcloud.mesh.dcs.dao.SchedulerJobDao;
import com.gcloud.mesh.dcs.dao.SchedulerStepDao;
import com.gcloud.mesh.dcs.entity.SchedulerStepEntity;
import com.gcloud.mesh.dcs.enums.SchedulerJobStatus;
import com.gcloud.mesh.dcs.enums.SchedulerStepStatus;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.jeecg.common.exception.MyBusinessException;
import org.jeecg.common.util.IdGen;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.UUIDGenerator;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.security.SecureRandom;
import java.util.Date;
import java.util.List;

@Slf4j
@Aspect
@Component
public class MigrationSchedulerJobStepAop {

	@Autowired
	private SchedulerStepDao schedulerStepDao;

	@Autowired
	private SchedulerJobDao schedulerJobDao;

	@Around(value = "execution(* com.gcloud.mesh.dcs.chain.IMigrationStepChain.doExec(..))")
	public Object around(ProceedingJoinPoint joinPoint) {

		String stepId = String.valueOf(IdGen.get().nextId());
		Object[] arguments = joinPoint.getArgs();
		String schedulerJobId = arguments[0].toString();
		String attach = arguments[1].toString();
		try {
			this.saveStepLog(stepId,schedulerJobId,arguments[3],attach);
			return joinPoint.proceed();
		} catch (Throwable e) {
			log.error("MigrationSchedulerJobStepAop异常",e);
			if(e instanceof MyBusinessException){
				schedulerStepDao.updateStepStatusById(stepId,SchedulerStepStatus.FAIL,((MyBusinessException) e).getErrorMsg());
			}else {
				schedulerStepDao.updateStepStatusById(stepId,SchedulerStepStatus.FAIL,e.getMessage());
			}
			schedulerJobDao.updateJobStatusById(schedulerJobId, SchedulerJobStatus.FAIL);
		}
		return null;
	}

	private void saveStepLog(String stepId,String schedulerJobId, Object proxy, String attach) throws Exception{
		String processClass = ((IMigrationStepChain)proxy).getClass().getName();
		if(AopUtils.isCglibProxy(proxy)) {
			Object cglibProxyTargetObject = this.getCglibProxyTargetObject(proxy);
			processClass = ((IMigrationStepChain)cglibProxyTargetObject).getClass().getName();
		}
		if(AopUtils.isJdkDynamicProxy(proxy)) {
			Object jdkProxyTargetObject = this.getJdkDynamicProxyTargetObject(proxy);
			processClass = ((IMigrationStepChain)jdkProxyTargetObject).getClass().getName();
		}
		Class<IMigrationStepChain> stepChainClass = (Class<IMigrationStepChain>) Class.forName(processClass);
		IMigrationStepChain stepChain = SpringContextUtils.getBean(stepChainClass);
		SchedulerStepEntity schedulerStepEntity = new SchedulerStepEntity();
		schedulerStepEntity.setId(stepId);
		schedulerStepEntity.setAttach(attach);
		schedulerStepEntity.setSchedulerType(stepChain.chainType());
		schedulerStepEntity.setSchedulerJobId(schedulerJobId);
		schedulerStepEntity.setStatus(SchedulerStepStatus.PROGRESS.getValue());
		schedulerStepEntity.setBeginTime(new Date());
		schedulerStepEntity.setName(stepChain.chainName(attach));
		schedulerStepEntity.setProcessClass(processClass);
		schedulerStepEntity.setSyncExec(stepChain.isSyncChain());
		schedulerStepDao.save(schedulerStepEntity);
	}


	private Object getCglibProxyTargetObject(Object proxy) throws Exception {
		Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
		h.setAccessible(true);
		Object dynamicAdvisedInterceptor = h.get(proxy);

		Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
		advised.setAccessible(true);
		Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
		return target;
	}

	private Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
		Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
		h.setAccessible(true);
		AopProxy aopProxy = (AopProxy) h.get(proxy);
		Field advised = aopProxy.getClass().getDeclaredField("advised");
		advised.setAccessible(true);
		Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
		return target;
	}

}
